package com.yilin.platform.base_core.commons.utils;

import org.dom4j.*;
import org.dom4j.io.OutputFormat;
import org.dom4j.io.SAXReader;
import org.dom4j.io.XMLWriter;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.lang.Nullable;

import javax.xml.bind.JAXBContext;
import javax.xml.bind.Marshaller;
import javax.xml.bind.Unmarshaller;
import java.io.StringReader;
import java.io.StringWriter;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;


public class XmlUtils {
	private static Logger logger = LoggerFactory.getLogger(XmlUtils.class);

	/**
	 * 【单层：循环XML】循环体的xml转List
	 *	传入不要包含根节点
	 * @param xml xml字符串
	 * @param cycName 循环节点名称
	 * */
	public static List<Map<String, Object>> loopXmlToList(String xml,String cycName) {
		xml = "<List>"+xml+"</List>";
		//获取根节点值
		List<Map<String, Object>> list = new ArrayList<>();
		Document doc = null;
		try {
			doc = DocumentHelper.parseText(xml);
		} catch (DocumentException e) {
			logger.error("xml字符串解析，失败 --> {}", e);
		}
		if (null == doc) {
			return list;
		}
		Element rootElement = doc.getRootElement();
		List<Element> listRoot = rootElement.elements();
		for (Element element : listRoot){
			if (!element.getName().equals(cycName)){
				logger.info("根目录下未找到指定的循环节点");
				break;
			}
			Map<String,Object> map = new HashMap<>();
            List<Element> fileds =element.elements();
			for (Element filed:fileds){
			    //TODO:数据中键值首字符已被小写
                map.put(filed.getName(),filed.getTextTrim());
            }
            list.add(map);
		}
		return list;
	}

	/**
	 * 【多层】xml格式字符串转换为map
	 *
	 * @param xml xml字符串
	 * @return 第一个为Root节点，Root节点之后为Root的元素，如果为多层，可以通过key获取下一层Map
	 */
	public static Map<String, Object> multilayerXmlToMap(String xml) {
		Document doc;
		try {
			doc = DocumentHelper.parseText(xml);
		} catch (DocumentException e) {
			e.printStackTrace();
			return null;
		}
		Map<String, Object> map = new HashMap<>();
		if (doc == null) {
			return map;
		}
		Element rootElement = doc.getRootElement();
		parseElement(rootElement, map);
		return map;
	}

	@SuppressWarnings("unchecked")
	private static void parseElement(Element element, Map<String, Object> map) {
		// 处理元素的属性
		List<Attribute> attributes = element.attributes();
		for (Attribute attribute : attributes) {
			map.put(attribute.getName(), attribute.getValue());
		}

		// 处理子元素
		List<Element> children = element.elements();
		for (Element child : children) {
			String childName = child.getName();
			Object childValue = map.get(childName);
			if (childValue == null) {
				if (child.elements().isEmpty()) {
					map.put(childName, child.getText());
				} else {
					Map<String, Object> childMap = new HashMap<>();
					parseElement(child, childMap);
					map.put(childName, childMap);
				}
			} else {
				if (childValue instanceof List) {
					((List<Map<String, Object>>) childValue).add(parseChild(child));
				} else {
					List<Map<String, Object>> childList = new ArrayList<>();
					childList.add((Map<String, Object>) childValue);
					childList.add(parseChild(child));
					map.put(childName, childList);
				}
			}
		}
	}

	private static Map<String, Object> parseChild(Element element) {
		Map<String, Object> childMap = new HashMap<>();
		parseElement(element, childMap);
		return childMap;
	}

	/**
	 * multilayerXmlToMap核心方法，递归调用
	 *
	 * @param element 节点元素
	 * @param outmap 用于存储xml数据的map
	 */
	@SuppressWarnings("unchecked")
	private static void recursionXmlToMap(Element element, Map<String, Object> outmap) {
		// 得到根元素下的子元素列表
		List<Element> list = element.elements();
		int size = list.size();
		if (size == 0) {
			// 如果没有子元素,则将其存储进map中
			outmap.put(element.getName(), element.getTextTrim());
		} else {
			// innermap用于存储子元素的属性名和属性值
			Map<String, Object> innermap = new HashMap<>();
			// 遍历子元素
			list.forEach(childElement -> recursionXmlToMap(childElement, innermap));
			outmap.put(element.getName(), innermap);
		}
	}


	/**
	 * 【多层】map转换为xml格式字符串
	 *
	 * @param map 需要转换为xml的map
	 * @param isCDATA 是否加入CDATA标识符 true:加入 false:不加入
	 * @return xml字符串
	 */
	public static String multilayerMapToXml(Map<String, Object> map, boolean isCDATA){
		String parentName = "xml";
		Document doc = DocumentHelper.createDocument();
		doc.addElement(parentName);
		String xml = recursionMapToXml(doc.getRootElement(), parentName, map, isCDATA);
		return formatXML(xml);
	}

	/**
	 * multilayerMapToXml核心方法，递归调用
	 *
	 * @param element 节点元素
	 * @param parentName 根元素属性名
	 * @param map 需要转换为xml的map
	 * @param isCDATA 是否加入CDATA标识符 true:加入 false:不加入
	 * @return xml字符串
	 */
	@SuppressWarnings("unchecked")
	private static String recursionMapToXml(Element element, String parentName, Map<String, Object> map, boolean isCDATA) {
		Element xmlElement = element.addElement(parentName);
		map.keySet().forEach(key -> {
			Object obj = map.get(key);
			if (obj instanceof Map) {
				recursionMapToXml(xmlElement, key, (Map<String, Object>)obj, isCDATA);
			} else {
				String value = obj == null ? "" : obj.toString();
				if (isCDATA) {
					xmlElement.addElement(key).addCDATA(value);
				} else {
					xmlElement.addElement(key).addText(value);
				}
			}
		});
		return xmlElement.asXML();
	}

	/**
	 * 格式化xml,显示为容易看的XML格式
	 *
	 * @param xml 需要格式化的xml字符串
	 * @return
	 */
	public static String formatXML(String xml) {
		String requestXML = null;
		try {
			// 拿取解析器
			SAXReader reader = new SAXReader();
			Document document = reader.read(new StringReader(xml));
			if (null != document) {
				StringWriter stringWriter = new StringWriter();
				// 格式化,每一级前的空格
				OutputFormat format = new OutputFormat("\t", true);
				// xml声明与内容是否添加空行
				format.setNewLineAfterDeclaration(false);
				// 是否设置xml声明头部
				format.setSuppressDeclaration(false);
				// 是否分行
				format.setNewlines(true);
				XMLWriter writer = new XMLWriter(stringWriter, format);
				writer.write(document);
				writer.flush();
				writer.close();
				requestXML = stringWriter.getBuffer().toString();
			}
			return requestXML;
		} catch (Exception e) {
			logger.error("格式化xml，失败 --> {}", e);
			return null;
		}
	}

	/**
	 * JavaBean转换成XML
	 */
	public static String convertToXml(Object obj, String encoding) {
		String result = null;
		try {
			JAXBContext context = JAXBContext.newInstance(obj.getClass());
			Marshaller marshaller = context.createMarshaller();
			marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true);
			marshaller.setProperty(Marshaller.JAXB_ENCODING, encoding);
			StringWriter writer = new StringWriter();
			marshaller.marshal(obj, writer);
			result = writer.toString();
		} catch (Exception ex) {
			return  null;
		}
		return result;
	}


	/**
	 * JavaBean转换成XML，报文头
	 */
	public static String convertToXml(Object obj, String encoding,Boolean isXmlHead) {
		String result = null;
		if(isXmlHead==null) {
			isXmlHead=false;
		}
		try {
			JAXBContext context = JAXBContext.newInstance(obj.getClass());
			Marshaller marshaller = context.createMarshaller();
			marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true);
			
			if(isXmlHead==false) {
				marshaller.setProperty(Marshaller.JAXB_FRAGMENT, true);//不含生成默认报文头-格式
			}else {
				marshaller.setProperty(Marshaller.JAXB_ENCODING, encoding);//格式
			}
			marshaller.setProperty(Marshaller.JAXB_FRAGMENT, true);
			StringWriter writer = new StringWriter();
			marshaller.marshal(obj, writer);
			result = writer.toString();
		} catch (Exception ex) {
			return  null;
		}
		return result;
	}

	
	/**
	 * XML转换成JavaBean
	 */
	@SuppressWarnings("unchecked")
	public static <T> T converyToJavaBean(String xml, Class<T> c) {
		T t = null;
		try {
			JAXBContext context = JAXBContext.newInstance(c);
			Unmarshaller unmarshaller = context.createUnmarshaller();
			t = (T) unmarshaller.unmarshal(new StringReader(xml));
		} catch (Exception ex) {
			return  null;
		}
		return t;
	}
	public static String mapToXml(@Nullable Map<String, Object> map) {
		if(map==null)return null;
		StringBuilder xml = new StringBuilder();
		buildXml(map, xml);
		return xml.toString();
	}

	private static void buildXml(Map<String, Object> map, StringBuilder xml) {
		for (Map.Entry<String, Object> entry : map.entrySet()) {
			String key = entry.getKey();
			Object value = entry.getValue();
			if (value instanceof Map) {
				xml.append("<").append(key).append(">");
				buildXml((Map<String, Object>) value, xml);
				xml.append("</").append(key).append(">");
			} else if (value instanceof List) {
				List<?> list = (List<?>) value;
				for (Object item : list) {
					xml.append("<").append(key).append(">");
					if (item instanceof Map) {
						buildXml((Map<String, Object>) item, xml);
					} else {
						xml.append(item);
					}
					xml.append("</").append(key).append(">");
				}
			} else {
				xml.append("<").append(key).append(">").append(value).append("</").append(key).append(">");
			}
		}
	}
	

}
